home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
keyb
/
trecal12.zip
/
INTPROTO.TXT
< prev
next >
Wrap
Text File
|
1991-11-09
|
21KB
|
511 lines
IBM'S INTERRUPT-SHARING PROTOCOL
by Chris Dunford
8/6/91
In the PS/2 BIOS Interface Technical Reference, IBM has suggested a
protocol for the sharing of system interrupts. Although the protocol
was intended to allow sharing of hardware interrupts, it is equally
usable for software interrupts.
One of the features of the interrupt sharing protocol is that it permits
a resident program to "unhook" itself from an interrupt even if it is
not the first interrupt handler in the chain of handlers. The benefit
of this should be immediately apparent to developers of TSRs. It is a
commonplace in TSR manuals to see verbiage along these lines:
Program X can be unloaded from memory by typing ... at the DOS
prompt. For this to work, however, X must be the last TSR loaded.
If other resident software is loaded after X, you cannot unload X.
The interrupt sharing protocol eliminates this restriction.
However, for the protocol to work, it must be followed by the majority
of TSR writers. To date, this has not occurred. Because the protocol
is easy to implement and inexpensive in terms of memory, I feel that at
least part of the reason for this must be that the protocol has not been
widely publicized; most DOS programmers are simply unaware of it. I am
offering this document as a modest attempt to let DOS programmers know
that a solution exists for a longstanding problem.
Let me add as a caveat that I do not have and have not examined the
primary source for this information--the PS/2 BIOS reference. Most of
the information in this paper was gleaned from other sources, augmented
by my own experiences in writing TSRs. The most recent writeup as of
this date (August 6, 1991) was in the 7/91 issue of the Microsoft
Systems Journal.
This document is not copyrighted. Its distribution in any form is
encouraged (please try to avoid making a profit on it). If you make
changes, please make sure they are clearly marked so that I get blame
only for my own errors and credit only where it is due. Please try to
get any changes, amplifications, corrections, etc., back to me so that a
clean copy can be redistributed if necessary.
The perpetrator of this document is:
Chris Dunford
The Cove Software Group
PO Box 1072
Columbia, MD 21044
301/992-9371
CompuServe: 76703,2002
Internet: 76703.2002@compuserve.com
THE PROBLEM
-----------
The vast majority of terminate-and-stay-resident (TSR) programs need to
intercept one or more hardware or software interrupts. Shown below is
typical code for accomplishing this, assuming that the interrupt to be
"hooked" is the DOS service interrupt (INT 21h):
(data)
OldInt21 dd ?
(installation code)
; Save current INT 21h vector
mov ax,3521h
int 21h
mov word ptr OldInt21,bx
mov word ptr OldInt21+2,es
; Set new INT 21h vector
mov dx,offset NewInt21
mov ax,2521h
int 21h
...
(interrupt handler)
NewInt21:
(perform processing as required)
jmp OldInt21
The installation code saves the current contents of the INT 21h vector
in a 32-bit variable called OldInt21, which is known as a "downward
link" or "downlink" because it provides a link "downward" in the chain
of interrupt handlers. The installation then sets the INT 21h vector to
point to its own interrupt handler at NewInt21. When an INT 21h call is
subsequently issued, execution is routed to NewInt21, which performs
whatever processing it needs to do. It then executes a far jump to the
address in OldInt21, allowing previously installed TSRs (and DOS, of
course) to do their work. The flow of control looks like this if only
one TSR is loaded:
vector OldInt21
application (int 21h) -----------> TSR ----------> DOS
To unload itself, the TSR simply resets the INT 21h vector to its
initial contents (i.e., the address in OldInt21). The TSR's interrupt
handler is now no longer in the chain, and the TSR can be safely
unloaded:
vector
application (int 21h) -----------> DOS
This scheme works fairly well until the situation arises where more than
one program tries to hook the same vector:
vector OldInt21A OldInt21B
app -----------> TSR A ---------> TSR B ---------> DOS
This also works fine--until TSR B wants to unload itself. B cannot
follow its normal procedure and replace the INT 21h vector with the
contents of its OldInt21. If it did, the interrupt chain would look
like this:
vector
app -----------> DOS
The problem, obviously, is that TSR A has been unceremoniously removed
from the interrupt chain; it has been disabled without notice, even
though it remains in memory. This is obviously an unsatisfactory--and
quite possibly dangerous--situation.
Nor can TSR B simply unload itself without fixing INT 21h. TSR A would
still have TSR B's address stored in its OldInt21; when it has completed
its processing of an INT 21h call, it will jump to the address where TSR
B was at one time--but is no longer--loaded. The only unpredictable
aspect of the result is which kind of reboot (hard or soft) will be
required.
TSR B's only option is to wave its hands and notify the user that it
cannot be unloaded. This is satisfying to the programmer ("We told you
that you can't do this") but not to the user.
The root of the problem is that TSR A has TSR B's address, but not vice
versa. If TSR B knew where TSR A was keeping its (B's) address, the
resolution would be simple: B could simply copy its downlink into A's
downlink.
Here is a hypothetical memory map:
---------------------
VECTOR TABLE
0000:0084 INT21h vector = 1200:0240 --+
---------------------- |
|
---------------------- |
TSR A |
1200:0240 NewInt21 (int handler) <----+
...
1200:0642 OldInt21 = 1000:0296 ---+
---------------------- |
|
---------------------- |
TSR B |
1000:0296 NewInt21 (int handler) <--+
...
1000:0415 OldInt21 = 0070:1234 ---+
---------------------- |
|
---------------------- |
DOS |
0070:1234 INT 21h entry point <--+
...
----------------------
The vector table entry for INT 21h points to TSR A's interrupt handler
at 1200:0240. TSR A's OldInt21 contains TSR B's interrupt handler
address (1000:0296); when A has completed its work, it jumps to B at
that address. B has DOS's address (0070:1234) in its OldInt21; when it
has finished, it jumps to DOS's address.
To take itself out of the chain, all B would have to do would be to put
its downward link (DOS's address, contained in B's OldInt21) into A's
downward link (which currently contains B's address):
---------------------
VECTOR TABLE
0000:0084 INT21h vector = 1200:0240 --+
---------------------- |
|
---------------------- |
TSR A |
1200:0240 NewInt21 (int handler) <----+
...
1200:0642 OldInt21 = 0070:1234 ---+ <=== change made here
---------------------- |
|
---------------------- |
TSR B |
1000:0296 NewInt21 (int handler) |
...